package com.googlecode.maurersmarbles;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import android.graphics.Point;

public class GameLogic {
	
	/**
	 * The number of playable spaces on the board.
	 */
	public static final int BOARD_SPACES = 56;
	/**
	 * The indexes of the home entry space for each player.
	 */
	public static final int HOME_ENTRY[] = {54, 12, 26, 40};
	/**
	 * The max indexes of the home spaces for each player.
	 */
	public static final int HOME_MAX[] = {59, 63, 67, 71};
	/**
	 * The min indexes of the home spaces for each player.
	 */
	public static final int HOME_MIN[] = {56, 60, 64, 68};
	/**
	 * The map that is used to store the index to cell mapping.
	 */
	protected static final Map<Integer, Point> INDEX_TO_CELL_MAP = new TreeMap<Integer, Point>();
	/**
	 * The number of marbles each player has.
	 */
	public static final int MARBLES_PER_PLAYER = 4;
	/**
	 * The number of players.
	 */
	public static final int NUMBER_OF_PLAYERS = 4;
	/**
	 * The indexes of the safe space for each player.
	 */
	public static final int SAFE[] = {0, 14, 28, 42};
	/**
	 * The max indexes of the start spaces for each player.
	 */
	public static final int START_MAX[] = {75, 79, 83, 87};
	/**
	 * The min indexes of the start spaces for each player.
	 */
	public static final int START_MIN[] = {72, 76, 80, 84};
	
	/**
	 * Returns the list of plays for the given cards.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @param cards the available cards
	 * @return the list of plays for the given cards
	 */
	public static List<Play> getPlays(Byte[][] marbles, int player, List<Card> cards) {
		List<Play> plays = new ArrayList<Play>();
		for(Card card : cards) {
			plays.addAll(getPlays(marbles, player, card));
		}
		return plays;
	}
	
	/**
	 * Returns the list of plays for the given card.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @param card the card
	 * @return the list of plays for the given card
	 */
	public static List<Play> getPlays(Byte[][] marbles, int player, Card card) {
		
		List<Play> plays = new ArrayList<Play>();
		
		switch(card.getId()) {
		
			case Card.CARD_ID_7:
				
				if(!hasMarblesOnBoard(marbles, player)) {
					break;
				}
				Card tmpCard1;
				Card tmpCard2;
				for(int i = 1; i <= 6; i++) {
					int j = 7 - i;
					tmpCard1 = new Card(-1, i);
					tmpCard2 = new Card(-1, j);
					for(int k = 0; k < 2; k++) {
						for(int l = 0; l < 2; l++) {					
							List<Play> tmpPlays1 = getPlays(marbles, (k == 0 ? player : getPartnerNumber(player)), tmpCard1);
							for(Play play1 : tmpPlays1) {
								Byte[][] tmpMarbles = applyPlay(marbles, play1);
								List<Play> tmpPlays2 = getPlays(tmpMarbles, (l == 0 ? player : getPartnerNumber(player)), tmpCard2);
								for(Play play2 : tmpPlays2) {
									if(((SinglePlay)play1).player == ((SinglePlay)play2).player
											&& ((SinglePlay)play1).marble == ((SinglePlay)play2).marble) {
										continue;
									}									
									plays.add(new DoublePlay(
									new SinglePlay(card, ((SinglePlay)play1).player, ((SinglePlay)play1).marble, ((SinglePlay)play1).newIndex),
									new SinglePlay(card, ((SinglePlay)play2).player, ((SinglePlay)play2).marble, ((SinglePlay)play2).newIndex)));
								}
							}							
						}
					}
				}
				tmpCard1 = new Card(-1, 7);
				for(int i = 0; i < 2; i++) {
					List<Play> tmpPlays = getPlays(marbles, (i == 0 ? player : getPartnerNumber(player)), tmpCard1);
					for(Play play : tmpPlays) {
						plays.add(new SinglePlay(card, ((SinglePlay)play).player, ((SinglePlay)play).marble, ((SinglePlay)play).newIndex));
					}
				}
				// TODO Need to remove duplicate plays
				break;
		
			case Card.CARD_ID_J:	
				
				// create swap combinations
				for(int i = 0; i < MARBLES_PER_PLAYER; i++) {							
					// make sure this marble is on the board in play
					if(!isMarbleOnBoardInPlay(marbles, player, i)) {
						continue;
					}							
					for(int dp = 0; dp < NUMBER_OF_PLAYERS; dp++) {								
						// cannot swap marble with our own
						if(dp == player) {
							continue;
						}								
						for(int dm = 0; dm < MARBLES_PER_PLAYER; dm++) {
							// marble must be on the board in play
							if(isMarbleOnBoardInPlay(marbles, dp, dm)) {
								// cannot swap safe marble unless it is our partners
								if(!isMarbleInSafe(marbles, dp, dm) || dp == getPartnerNumber(player)) {
									Play play = new SinglePlay(card, player, i, marbles[dp][dm]);
									plays.add(play);
								}										
							}									
						}								
					}							
				}											
				break;
				
			case Card.CARD_ID_A:
				// falls through
			case Card.CARD_ID_K:
				if(hasMarbleInStart(marbles, player) && !hasMarbleInSafe(marbles, player)) {
					Play play = new SinglePlay(card, player, getNextStartMarble(marbles, player), SAFE[player]);
					plays.add(play);
				}					
				if(card.getId() == Card.CARD_ID_K){
					break;
				}
				// CARD_ID_A falls through
				
			default:
				
				// check to see if the card seems playable
				if(card.getValue() == Card.CARD_ID_4) {
					if(getNumberOfMarblesOnBoard(marbles, player) - getNumberOfMarblesInHome(marbles, player) <= 0) {
						break;
					}
				}
				else if(!hasMarblesOnBoard(marbles, player)) {
					break;
				}
				
				for(int i = 0; i < MARBLES_PER_PLAYER; i++) {
					
					int index = marbles[player][i];
					
					// check to see if we are in a home space
					if(isMarbleInHome(marbles, player, i)) {						
						if(card.getValue() < 0 || (index + card.getValue() > HOME_MAX[player])) {
							continue;
						}
						boolean validMove = true;
						for(int j = index + 1; j <= index + card.getValue(); j++) {
							if(isPlayerMarbleInSpace(marbles, player, j)) {
								validMove = false;
								break;
							}
						}
						if(validMove) {
							Play play = new SinglePlay(card, player, i, index + card.getValue());
							plays.add(play);
						}
						continue;							
					}
					
					// check to make sure this marble is on the board
					if(!isMarbleOnBoardInPlay(marbles, player, i)) {
						continue;
					}
					
					boolean wrapped = false;						
					int newIndex = index + card.getValue();
					if(newIndex >= BOARD_SPACES) {
						newIndex = newIndex - BOARD_SPACES;
						wrapped = true;
					}
					else if(newIndex < 0) {
						newIndex = newIndex + BOARD_SPACES;
						wrapped = true;
					}

					// check to see if we passed our home entry space						
					if(newIndex > HOME_ENTRY[player]) {
						int diff = newIndex - HOME_ENTRY[player];								
						// cannot move more than 4 positions in home spaces
						if(diff > 4) {
							continue;
						}			
						boolean validMove = true;
						for(int j = 0; j < diff; j++) {
							if(isPlayerMarbleInSpace(marbles, player, HOME_MIN[player] + j)) {
								validMove = false;
								break;
							}
						}
						if(!validMove) {
							continue;
						}																
					}
						
					// check to see if we landed on our marble
					if(isOurMarbleInSpace(marbles, player, newIndex)) {
						continue;
					}
					
					// check to see if we landed on a safe marble
					boolean validMove = true;
					for(int j = 0; j < NUMBER_OF_PLAYERS; j++) {
						// TODO Need to check for non safe marble in safe space
						if(newIndex == SAFE[j] && hasMarbleInSafe(marbles, j)) {
							validMove = false;
							break;
						}
					}
					if(!validMove) {
						continue;
					}
					
					// check to see if we passed a safe marble
					validMove = true;
					for(int j = 0; j < NUMBER_OF_PLAYERS; j++) {
						if(hasMarbleInSafe(marbles, j)) {
							if(j > 0) {									
								if(((index < SAFE[j]) && (newIndex > SAFE[j])) || ((index > SAFE[j]) && (newIndex < SAFE[j]))) {
									validMove = false;
									break;
								}
							}
							else if(wrapped) {
								validMove = false;
								break;
							}
						}
					}
					if(!validMove) {
						continue;
					}
				
					// all tests passed, add this play
					Play play = new SinglePlay(card, player, i, newIndex);
					plays.add(play);
					
				}
				
				break;
		
		}
		
		return plays;
		
	}
	
	/**
	 * Returns whether or not the given marble is in the safe space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @param marble the marble number
	 * @return whether or not the given marble is in the safe space
	 */
	protected static boolean isMarbleInSafe(Byte[][] marbles, int player, int marble) {
		return marbles[player][marble] == SAFE[player];
	}
	
	/**
	 * Returns whether or not the given player has a marble in the safe space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @return whether or not the given player has a marble in the safe space
	 */
	protected static boolean hasMarbleInSafe(Byte[][] marbles, int player) {
		// TODO Need to account for non safe marble in safe space
		for(int i = 0; i < marbles[player].length; i++) {
			if(marbles[player][i] == SAFE[player]) {
				return true;
			}
		}		
		return false;
	}
		
	/**
	 * Returns whether or not the given players marble is in the given space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @param space the space number
	 * @return whether or not the given players marble is in the given space
	 */
	protected static boolean isPlayerMarbleInSpace(Byte[][] marbles, int player, int space) {
		for(int i = 0; i < MARBLES_PER_PLAYER; i++) {
			if(marbles[player][i] == space) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Returns whether or not our marble is in the given space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @param space the space number
	 * @return whether or not our marble is in the given space
	 */
	protected static boolean isOurMarbleInSpace(Byte[][] marbles, int player, int space) {
		return isPlayerMarbleInSpace(marbles, player, space)
				|| isPlayerMarbleInSpace(marbles, getPartnerNumber(player), space);
	}
	
	/**
	 * Returns whether or not the given marble is in a home space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @param marble the marble number to check
	 * @return whether or not the given marble is in a home space
	 */
	protected static boolean isMarbleInHome(Byte[][] marbles, int player, int marble) {
		if(marbles[player][marble] >= HOME_MIN[player] && marbles[player][marble] <= HOME_MAX[player]) {
			return true;
		}
		return false;
	}
	
	/**
	 * Returns the number of marbles in a home space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @return the number of marbles in a home space
	 */
	protected static int getNumberOfMarblesInHome(Byte[][] marbles, int player) {
		int count = 0;
		for(int i = 0; i < marbles[player].length; i++) {
			if(isMarbleInHome(marbles, player, i)) {
				count++;
			}
		}
		return count;
	}
	
	/**
	 * Returns whether or not the given marble is in a start space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @param marble the marble number to check
	 * @return whether or not the given marble is in a start space
	 */
	protected static boolean isMarbleInStart(Byte[][] marbles, int player, int marble) {
		if(marbles[player][marble] >= START_MIN[player] && marbles[player][marble] <= START_MAX[player]) {
			return true;
		}
		return false;
	}
	
	/**
	 * Returns whether or not there is a marble in a start space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @return whether or not there is a marble in a start space
	 */
	protected static boolean hasMarbleInStart(Byte[][] marbles, int player) {
		return getNumberOfMarblesInStart(marbles, player) > 0;
	}
	
	/**
	 * Returns the number of marbles in a start space.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @return the number of marbles in a start space
	 */
	protected static int getNumberOfMarblesInStart(Byte[][] marbles, int player) {
		int count = 0;
		for(int i = 0; i < marbles[player].length; i++) {
			if(isMarbleInStart(marbles, player, i)) {
				count++;
			}
		}
		return count;
	}
	
	/**
	 * Returns whether or not the given marble is on the board and in play.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @param marble the marble number
	 * @return whether or not the given marble is on the board and in play
	 */
	protected static boolean isMarbleOnBoardInPlay(Byte[][] marbles, int player, int marble) {
		return marbles[player][marble] < BOARD_SPACES;
	}
	
	/**
	 * Returns the number of marbles that are in play on the board.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @return the number of marbles that are in play on the board
	 */
	protected static int getNumberOfMarblesOnBoard(Byte[][] marbles, int player) {
		return MARBLES_PER_PLAYER - getNumberOfMarblesInStart(marbles, player);
	}
	
	/**
	 * Returns whether or not there are marbles in play on the board.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @return whether or not there are marbles in play on the board
	 */
	protected static boolean hasMarblesOnBoard(Byte[][] marbles, int player) {
		return getNumberOfMarblesOnBoard(marbles, player) > 0;
	}

	/**
	 * Returns the number of unsafe marbles on the board.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @return the number of unsafe marbles on the board
	 */
	protected static int getNumberOfMarblesUnsafeOnBoard(Byte[][] marbles, int player) {
		return getNumberOfMarblesOnBoard(marbles, player) - (hasMarbleInSafe(marbles, player) ? 1 : 0) - getNumberOfMarblesInHome(marbles, player);
	}
	
	/**
	 * Returns the next marble to take out of the start spaces.
	 * @param marbles the marbles array
	 * @param player the player number
	 * @return the next marble to take out of the start spaces
	 */
	protected static int getNextStartMarble(Byte[][] marbles, int player) {
		for(int i = 0; i < marbles[player].length; i++) {
			if(isMarbleInStart(marbles, player, i)) {
				return i;
			}
		}
		return MARBLES_PER_PLAYER;
	}
	
	/**
	 * Returns the partner number for the given player number.
	 * @param player the player number
	 * @return the partner number for the given player number
	 */
	protected static int getPartnerNumber(int player) {
		switch(player) {
			case 0:
				return 2;
			case 1:
				return 3;
			case 2:
				return 0;
			case 3:
				return 1;
		}
		return -1;
	}
	
	/**
	 * Returns a new copy of the marbles array.
	 * @param marbles the marbles array to copy
	 * @return a new copy of the marbles array
	 */
	protected static Byte[][] copyMarbles(Byte[][] marbles) {
		Byte[][] newMarbles = new Byte[NUMBER_OF_PLAYERS][MARBLES_PER_PLAYER];
		for(int i = 0; i < NUMBER_OF_PLAYERS; i++) {
			for(int j = 0; j < MARBLES_PER_PLAYER; j++) {
				newMarbles[i][j] = marbles[i][j];
			}
		}
		return newMarbles;
	}

	/**
	 * Returns a marbles array with all of the marbles in their starting position.
	 * @return a marbles array with all of the marbles in their starting position
	 */
	public static Byte[][] getStartMarbles() {
		Byte[][] marbles = new Byte[NUMBER_OF_PLAYERS][MARBLES_PER_PLAYER];
		for(int i = 0; i < NUMBER_OF_PLAYERS; i++) {
			for(int j = 0; j < MARBLES_PER_PLAYER; j++) {
				marbles[i][j] = (byte)(START_MIN[i] + j);
			}
		}
		return marbles;
	}
	
	/**
	 * Applies the given play and returns a new marbles array that is updated.
	 * @param marbles the marbles array
	 * @param play the play to apply
	 * @return a new marbles array 
	 */
	public static Byte[][] applyPlay(Byte[][] marbles, Play play) {
		Byte[][] newMarbles = copyMarbles(marbles);
		if(play instanceof DoublePlay) {
			applySinglePlay(newMarbles, ((DoublePlay)play).play1);
			applySinglePlay(newMarbles, ((DoublePlay)play).play2);
		}
		else {
			applySinglePlay(newMarbles, (SinglePlay)play);
		}
		return newMarbles;
	}
	
	/**
	 * Applies the given play and returns a new marbles array that is updated.
	 * @param marbles the marbles array
	 * @param play the play to apply
	 */
	protected static void applySinglePlay(Byte[][] marbles, SinglePlay play) {		
		int[] playerMarble = getPlayerAndMarbleInSpace(marbles, play.newIndex);
		if(playerMarble != null) {
			if(play.card.getId() == Card.CARD_ID_J) {
				marbles[playerMarble[0]][playerMarble[1]] = marbles[play.player][play.marble];
			}
			else {
				marbles[playerMarble[0]][playerMarble[1]] = (byte)(START_MIN[playerMarble[0]] + (getNextStartMarble(marbles, playerMarble[0]) - 1));
			}
		}		
		// update the marble
		marbles[play.player][play.marble] = (byte)play.newIndex;	
	}
	
	/**
	 * Returns the player and the marble in the given space, null if there is not one.
	 * @param marbles the marbles array
	 * @param space the space to check
	 * @return the player and the marble in the given space, null if there is not one
	 */
	protected static int[] getPlayerAndMarbleInSpace(Byte[][] marbles, int space) {
		int[] playerMarble = null;
		for(int i = 0; i < NUMBER_OF_PLAYERS; i++) {
			for(int j = 0; j < MARBLES_PER_PLAYER; j++) {		
				if(marbles[i][j] == space) {
					playerMarble = new int[2];
					playerMarble[0] = i;
					playerMarble[1] = j;
					i = NUMBER_OF_PLAYERS;
					break;				
				}
			}
		}
		return playerMarble;		
	}
	
	/**
	 * Returns the cell for the given index.
	 * @param index the index to get the cell for
	 * @return the cell for the given index
	 */
	public static Point getCellForIndex(int index) {
		return INDEX_TO_CELL_MAP.get(index);
	}
	
	/**
	 * Returns the index for the given cell. 
	 * @param point the cell to get the index for
	 * @return the index for the given cell
	 */
	public static int getIndexForCell(Point point) {
		for(Map.Entry<Integer, Point> entry : INDEX_TO_CELL_MAP.entrySet()) {
			if(entry.getValue().equals(point)) {
				return entry.getKey();
			}
		}
		return -1;
	}
	
	static {
		/**
		 *    | 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14
		 * ---|---------------------------------------------
		 * 00 |                24 25 26 27 28 
		 * 01 |    76 77       23    64    29       80 81
		 * 02 |                22    65    30       
		 * 03 |    78 79       21    66    31       82 83
		 * 04 |                20    67    32
		 * 05 | 14 15 16 17 18 19          33 34 35 36 37 38
		 * 06 | 13                                        39
		 * 07 | 12 60 61 62 63                71 70 69 68 40
		 * 08 | 11                                        41
		 * 09 | 10 09 08 07 06 05          47 46 45 44 43 42
		 * 10 |                04    59    48
		 * 11 |    72 73       03    58    49       84 85
		 * 12 |                02    57    50       
		 * 13 |    74 75       01    56    51       86 87
		 * 14 |                00 55 54 53 52
		 */
		// playable spaces
		INDEX_TO_CELL_MAP.put(0, new Point(5, 14)); 
		INDEX_TO_CELL_MAP.put(1, new Point(5, 13));
		INDEX_TO_CELL_MAP.put(2, new Point(5, 12));
		INDEX_TO_CELL_MAP.put(3, new Point(5, 11));
		INDEX_TO_CELL_MAP.put(4, new Point(5, 10));
		INDEX_TO_CELL_MAP.put(5, new Point(5, 9));		
		INDEX_TO_CELL_MAP.put(6, new Point(4, 9));
		INDEX_TO_CELL_MAP.put(7, new Point(3, 9));
		INDEX_TO_CELL_MAP.put(8, new Point(2, 9));
		INDEX_TO_CELL_MAP.put(9, new Point(1, 9));			
		INDEX_TO_CELL_MAP.put(10, new Point(0, 9));			
		INDEX_TO_CELL_MAP.put(11, new Point(0, 8));
		INDEX_TO_CELL_MAP.put(12, new Point(0, 7));
		INDEX_TO_CELL_MAP.put(13, new Point(0, 6));
		INDEX_TO_CELL_MAP.put(14, new Point(0, 5));
		INDEX_TO_CELL_MAP.put(15, new Point(1, 5));
		INDEX_TO_CELL_MAP.put(16, new Point(2, 5));
		INDEX_TO_CELL_MAP.put(17, new Point(3, 5));
		INDEX_TO_CELL_MAP.put(18, new Point(4, 5));
		INDEX_TO_CELL_MAP.put(19, new Point(5, 5));
		INDEX_TO_CELL_MAP.put(20, new Point(5, 4));
		INDEX_TO_CELL_MAP.put(21, new Point(5, 3));
		INDEX_TO_CELL_MAP.put(22, new Point(5, 2));
		INDEX_TO_CELL_MAP.put(23, new Point(5, 1));
		INDEX_TO_CELL_MAP.put(24, new Point(5, 0));
		INDEX_TO_CELL_MAP.put(25, new Point(6, 0));
		INDEX_TO_CELL_MAP.put(26, new Point(7, 0));
		INDEX_TO_CELL_MAP.put(27, new Point(8, 0));
		INDEX_TO_CELL_MAP.put(28, new Point(9, 0));
		INDEX_TO_CELL_MAP.put(29, new Point(9, 1));
		INDEX_TO_CELL_MAP.put(30, new Point(9, 2));
		INDEX_TO_CELL_MAP.put(31, new Point(9, 3));
		INDEX_TO_CELL_MAP.put(32, new Point(9, 4));
		INDEX_TO_CELL_MAP.put(33, new Point(9, 5));
		INDEX_TO_CELL_MAP.put(34, new Point(10, 5));
		INDEX_TO_CELL_MAP.put(35, new Point(11, 5));
		INDEX_TO_CELL_MAP.put(36, new Point(12, 5));
		INDEX_TO_CELL_MAP.put(37, new Point(13, 5));
		INDEX_TO_CELL_MAP.put(38, new Point(14, 5));
		INDEX_TO_CELL_MAP.put(39, new Point(14, 6));
		INDEX_TO_CELL_MAP.put(40, new Point(14, 7));
		INDEX_TO_CELL_MAP.put(41, new Point(14, 8));
		INDEX_TO_CELL_MAP.put(42, new Point(14, 9));
		INDEX_TO_CELL_MAP.put(43, new Point(13, 9));
		INDEX_TO_CELL_MAP.put(44, new Point(12, 9));
		INDEX_TO_CELL_MAP.put(45, new Point(11, 9));
		INDEX_TO_CELL_MAP.put(46, new Point(10, 9));
		INDEX_TO_CELL_MAP.put(47, new Point(9, 9));
		INDEX_TO_CELL_MAP.put(48, new Point(9, 10));
		INDEX_TO_CELL_MAP.put(49, new Point(9, 11));
		INDEX_TO_CELL_MAP.put(50, new Point(9, 12));
		INDEX_TO_CELL_MAP.put(51, new Point(9, 13));
		INDEX_TO_CELL_MAP.put(52, new Point(9, 14));
		INDEX_TO_CELL_MAP.put(53, new Point(8, 14));
		INDEX_TO_CELL_MAP.put(54, new Point(7, 14));
		INDEX_TO_CELL_MAP.put(55, new Point(6, 14));
		// home spaces
		INDEX_TO_CELL_MAP.put(56, new Point(7, 13));
		INDEX_TO_CELL_MAP.put(57, new Point(7, 12));
		INDEX_TO_CELL_MAP.put(58, new Point(7, 11));
		INDEX_TO_CELL_MAP.put(59, new Point(7, 10));
		INDEX_TO_CELL_MAP.put(60, new Point(1, 7));
		INDEX_TO_CELL_MAP.put(61, new Point(2, 7));
		INDEX_TO_CELL_MAP.put(62, new Point(3, 7));
		INDEX_TO_CELL_MAP.put(63, new Point(4, 7));
		INDEX_TO_CELL_MAP.put(64, new Point(7, 1));
		INDEX_TO_CELL_MAP.put(65, new Point(7, 2));
		INDEX_TO_CELL_MAP.put(66, new Point(7, 3));
		INDEX_TO_CELL_MAP.put(67, new Point(7, 4));
		INDEX_TO_CELL_MAP.put(68, new Point(14, 7));
		INDEX_TO_CELL_MAP.put(69, new Point(13, 7));
		INDEX_TO_CELL_MAP.put(70, new Point(12, 7));
		INDEX_TO_CELL_MAP.put(71, new Point(11, 7));
		// start spaces
		INDEX_TO_CELL_MAP.put(72, new Point(1, 11));
		INDEX_TO_CELL_MAP.put(73, new Point(3, 11));
		INDEX_TO_CELL_MAP.put(74, new Point(1, 13));
		INDEX_TO_CELL_MAP.put(75, new Point(3, 13));
		INDEX_TO_CELL_MAP.put(76, new Point(1, 1));		
		INDEX_TO_CELL_MAP.put(77, new Point(3, 1));
		INDEX_TO_CELL_MAP.put(78, new Point(1, 3));
		INDEX_TO_CELL_MAP.put(79, new Point(3, 3));
		INDEX_TO_CELL_MAP.put(80, new Point(11, 1));			
		INDEX_TO_CELL_MAP.put(81, new Point(13, 1));
		INDEX_TO_CELL_MAP.put(82, new Point(11, 3));
		INDEX_TO_CELL_MAP.put(83, new Point(13, 3));
		INDEX_TO_CELL_MAP.put(84, new Point(11, 11));			
		INDEX_TO_CELL_MAP.put(85, new Point(13, 11));
		INDEX_TO_CELL_MAP.put(86, new Point(11, 13));
		INDEX_TO_CELL_MAP.put(87, new Point(13, 13));
	}
	
}
